ZgRNS Git Node
Commit dc603349e4a13699948034dcd4df1f77e8a07e77
Parents : 184d7e4
Author : varna9000 <39020101+varna9000@users.noreply.github.com>
Date : 2026-05-03T21:46:12+03:00
Fix multi-hop transport routing, add resource request retry
HDR_2 auto-routing: packet.pack() upgrades PKT_DATA to HDR_2 with
transport_id when destination is behind a transport node (path_table
lookup). HDR_2 branch now encrypts data packets (was announce-only,
causing bytes+NoneType crash).
Resource retry: receiver retries request every 10s up to 5 times if no
parts arrive (LoRa TX/RX turnaround causes first request loss).
Changes
3 files changed, 46 insertions(+), 0 deletions(-)
Diff
diff --git a/firmware/urns/link.py b/firmware/urns/link.py
index 8cd6e6b..8b9b2fa 100644
--- a/firmware/urns/link.py
+++ b/firmware/urns/link.py
@@ -443,6 +443,10 @@ class Link:
if self.status != Link.ACTIVE:
return
+ # Check resource request timeouts (retry if no parts arrived)
+ for r in self.incoming_resources:
+ r.check_request_timeout()
+
# Check stale grace period
if now - self.last_activity > Link.STALE_GRACE:
log("Link " + self.link_id.hex()[:8] + " stale, closing", LOG_VERBOSE)
@@ -700,6 +704,9 @@ class OutgoingLink:
return
if self.status != OutgoingLink.ACTIVE:
return
+ # Check resource request timeouts (retry if no parts arrived)
+ for r in self.incoming_resources:
+ r.check_request_timeout()
if time.time() - self.last_activity > 720: # STALE_GRACE
log("OutLink " + self.link_id.hex()[:8] + " stale, closing", LOG_VERBOSE)
self._close()
diff --git a/firmware/urns/packet.py b/firmware/urns/packet.py
index 99e4bb8..dc23452 100644
--- a/firmware/urns/packet.py
+++ b/firmware/urns/packet.py
@@ -114,6 +114,19 @@ class Packet:
def pack(self):
self.destination_hash = self.destination.hash
+
+ # Auto-upgrade to HDR_2 if destination is reachable via a transport
+ # node (learned from HDR_2 announces stored in Transport.path_table).
+ if (self.header_type == const.HDR_1
+ and self.transport_id is None
+ and self.packet_type == const.PKT_DATA):
+ from .transport import Transport
+ _tid = Transport.path_table.get(self.destination_hash)
+ if _tid is not None:
+ self.header_type = const.HDR_2
+ self.transport_id = _tid
+ self.flags = self._get_packed_flags()
+
self.header = b""
self.header += struct.pack("!B", self.flags)
self.header += struct.pack("!B", self.hops)
@@ -144,6 +157,11 @@ class Packet:
self.header += self.destination.hash
if self.packet_type == const.PKT_ANNOUNCE:
self.ciphertext = self.data
+ else:
+ # Encrypt data packets (same as HDR_1 path)
+ self.ciphertext = self.destination.encrypt(self.data)
+ if hasattr(self.destination, 'latest_ratchet_id'):
+ self.ratchet_id = self.destination.latest_ratchet_id
else:
raise OSError("Header type 2 requires transport ID")
diff --git a/firmware/urns/resource.py b/firmware/urns/resource.py
index 883a4e0..015071c 100644
--- a/firmware/urns/resource.py
+++ b/firmware/urns/resource.py
@@ -19,6 +19,8 @@ TIMEOUT = 120
HASHMAP_IS_EXHAUSTED = 0xFF
HASHMAP_IS_NOT_EXHAUSTED = 0x00
MAX_RESOURCE_SIZE = 16384 # 16KB — ESP32 memory safe
+REQUEST_RETRY_INTERVAL = 10 # seconds between request retries
+MAX_REQUEST_RETRIES = 5
# Resource flags
FLAG_ENCRYPTED = 0x01
@@ -180,6 +182,8 @@ class Resource:
r.parts = [None] * r.total_parts
r.received_count = 0
r.window_count = 0 # parts received since last request
+ r.last_request_at = 0
+ r.request_retries = 0
r.sdu = link.sdu
r.encrypted = None
r.expected_proof = None
@@ -251,9 +255,26 @@ class Resource:
req_data += self.hashmap[i]
self.window_count = 0
+ self.last_request_at = time.time()
+ self.request_retries += 1
self.link.send(req_data, const.CTX_RESOURCE_REQ)
log("Resource request: " + str(len(missing)) + " parts for " + self.hash.hex()[:8], LOG_DEBUG)
+ def check_request_timeout(self):
+ """(Receiver) Retry resource request if no parts arrived within interval."""
+ if self.status != TRANSFERRING:
+ return
+ if self.last_request_at == 0:
+ return
+ if time.time() - self.last_request_at < REQUEST_RETRY_INTERVAL:
+ return
+ if self.request_retries >= MAX_REQUEST_RETRIES:
+ log("Resource request max retries reached: " + self.hash.hex()[:8], LOG_ERROR)
+ self.cancel()
+ return
+ log("Resource request retry " + str(self.request_retries) + "/" + str(MAX_REQUEST_RETRIES) + " for " + self.hash.hex()[:8], LOG_DEBUG)
+ self.request_next()
+
def receive_part(self, data):
"""(Receiver) Receive a raw resource part."""
if self.status != TRANSFERRING:
Served by rngit 1.5.2 - Generated in 0.05s